package shm;

import java.awt.Point;
import java.io.*;
import java.util.Random;

/**
 * This class reads message from the specific serial port and save the message
 * to the serial buffer.
 */

/*
 * ������������������������������ ��1������23����������������������������������23�������������������� ��2����������������������������3
 * 7����������������21��������������21������������3 7����������������������
 * 
 * V2: 27������������������25����������������������������������
 * 
 * V3: 31������������x,y,z��������
 * 
 * V4: ��������������������������������������������������
*/

/*
 * ��������������������14
 *  0  1  2  3  4  5  6  7| 8  9 10 11 12 13
 * 01|00 00 00 01|00|00 01|01|00|00 00 00 00
 * 0: Source, 1 byte
 * 1: Sequence, 4 bytes
 * 5: Parent, 1 byte
 * 6: Etc, 2 bytes
 * ----------------------
 * 8: Command, 1 byte
 * 9: Status, 1 byte
 * 10: Parameter, 4 bytes
 * 
 * ��������������������21
 *  0  1  2  3  4  5  6  7| 8  9 10 11 12 13 14 15 16 17 18 19 20
 * 01|00 00 00 01|00|00 01|7F|00 00 00 00|00 11|00 22|00 33|00 44
 * 0: Source, 1 byte
 * 1: Sequence, 4 bytes
 * 5: Parent, 1 byte
 * 6: Etc, 2 bytes
 * ----------------------
 * 8: Command, 1 byte
 * 9: Global time, 4 byte
 * 13: X, 2 bytes
 * 15: Y, 2 bytes
 * 17: Z, 2 bytes
 * 19: External, 2 bytes
 */

public class ReadSerialThread extends Thread {

	private InputStream mInputStream;
	
	/* State: 
	 * 0 begin, 
	 * 1, read routing
	 * 2, read status
	 * 3, read data
	 * -1, exiting
	 */
	public static int state = 0;
	public static int nSeq = 0;

	public ReadSerialThread(InputStream in) {
		this.mInputStream = in;
	}
	
	// read a single byte.
	private byte logRead()
	{
		byte c = -1;
		try {
			c = (byte) mInputStream.read();
		} catch (IOException e) {
			e.printStackTrace();
		}
		Logger.instance().appendRaw(Logger.formatHex(c));
		return c;
	}
	
	// read some bytes
	private String logRead(byte[] b, int len)
	{
		int c = -1;
		int av = -1;
		
		// should wait for sufficient data
		while (av < len)
		{
			try {
				av = mInputStream.available();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		try {
			c = mInputStream.read(b, 0, len);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		if(c != len)
		{
			System.out.printf("c=%d, len = %d, how come\r\n",c, len);			
		}
		
		String t = Logger.formatHex(b, c);
		
		Logger.instance().appendRaw(t);
		return t;
	}
	
	private void processTopology(int id, int seq, int parent, int ext)
	{
		//System.out.printf("Topology: Node: %d. Sequence: %d. Parent: %d. Ext: %d.\r\n", id, seq, parent, ext);
		
		try {
			Topology.analyze(id, parent, ext, 0);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void processStatus(int id, int cmd, int status, int arg)
	{
		System.out.printf("Status: Node: %d. Cmd: %d. Status: %d. Arg: %d.\r\n", id, cmd, status, arg);
		
		String app = String.format("N=%d ", id);
		
		switch(status){
		case 1:
			app += "Ready";
			break;
		case 2:
		{
			app += String.format("ack cmd=%d, arg=%d", cmd, arg);			
			break;
		}
		case 3:
			app += "sample complete";
			break;
		case 4:
			app += String.format("init 4. arg=%d", arg);
			break;
		case 5:
			app += String.format("write usb 5. arg=%d", arg);
			break;
		case 6:
		{
			Random r = new Random();
			app += String.format("battery=%d", 70 + r.nextInt()%30);
			break;
		}
		default:
			app += String.format("status = %d", status);
			break;
		}
		
		
		String txt = MainUI.taStatus.getText();
		txt += "\r\n";
		txt += app;
		
		MainUI.taStatus.setText(txt);

//		int height=20;  
	    Point p = new Point();  
	    p.setLocation(0, 60000);  
	    MainUI.spStatus.getViewport().setViewPosition(p);
		
	}
	
	private void processData(int id, int time, int x, int y , int z, int ext)
	{
		// tell the main UI to update the data
		MainUI.os.newData(id, time, x, y, z, ext);
		//System.out.printf("Data: Node: %d. Time: %d. X: %d. Y: %d. Z: %d.\r\n", id, time, x, y, z, ext);
	
		//MainUI.os.Received(id, nSeq++, x, y, z, ext);
		//System.out.printf("Data: Node: %d. Time: %d. X: %d. Y: %d. Z: %d.\r\n", id, nSeq, x, y, z, ext);
	}


	/*
	 * Thread for receiving Data
	 */
	public void run() 
	{
		String proc_buf = "";
		byte[] buf = new byte[20];
		
		try
		{	
			while(ReadSerialThread.state != -1)
			{
				byte id = this.logRead();
				
				if (id == 0)	// skip the header
					continue;
				else
				{
					state = 1;
					proc_buf = Logger.formatHex(id);
				}
				
				// read routing, 8 bytes
				proc_buf += (this.logRead(buf, 7));
				
				// Topology process finished
				this.processTopology(id, Logger.intFromBytesLE(buf, 0, 4), buf[4], Logger.intFromBytes(buf, 5, 2));
				
				// read 1 byte header
				byte c = this.logRead();
				proc_buf += (Logger.formatHex(c));
				
				String rest = null;
				// ������������������
				if (c == 0x7F)
				{
					// data package, read 12 rest bytes
					rest = this.logRead(buf, 12);
					this.processData(id, 
							Logger.intFromBytesLE(buf, 0, 4)/256, 	// time
							Logger.intFromBytesLE(buf, 4, 2),	// x
							Logger.intFromBytesLE(buf, 6, 2),	// y
							Logger.intFromBytesLE(buf, 8, 2),	// z
							Logger.intFromBytesLE(buf, 10, 2)	// ext
							);
				}
				else if (c < 0x7)
				{
					// status package, read 5 rest bytes
					rest = this.logRead(buf, 5);
					this.processStatus(id, c, buf[0], Logger.intFromBytes(buf, 1, 4));
				}
				else {
					System.out.println("Protocol Error");
				}
				
				proc_buf += rest;
				
				//System.out.println(proc_buf);
				
				Logger.instance().appendProcessed(proc_buf);
				
			} // end while isRunning
		}catch(Exception e) {
			e.printStackTrace();
		}

	}
}

